package com.yxp.hard.astar;

import java.util.ArrayList;
import java.util.List;

public class AStarNode implements Comparable<AStarNode> {

    /*
     * F = G + H;
     *
     * F: 路径的开销下限。 G：从起始节点到当前节点的代价。 H：启发因子，即当前节点到目标节点的估计代价。
     */

    int F = Integer.MAX_VALUE;

    int G = 0;// Integer.MAX_VALUE;

    int getG() {
        return G;
    }

    int column;
    int row;
    int data;

    AStarNode searchParent;

    AStarNode(int row, int column, byte[][] mapData) {
        this.row = row;
        this.column = column;
        this.data = mapData[row][column];
    }

    /*
     * 计算当前节点的F值
     */
    void calculateF(AStarNode target) {
        if (searchParent != null)
            this.G = searchParent.G + predict(searchParent);
        this.F = this.G + this.predict(target);
    }

    /*
     * 计算当前节点的G值
     */
    int calculateG(AStarNode target) {
        return this.G + predict(target);
    }

    /*
     * 计算当前节点到目标节点的启发因子代价（H）
     */
    private int predict(AStarNode target) {
        int dx = Math.abs(this.getColumn() - target.getColumn());
        int dy = Math.abs(this.getRow() - target.getRow());
//        return Math.abs(dx - dy) * 5 + Math.min(dx, dy) * 7;

//         return dx + dy;// 曼哈顿距离

         return dx * dx + dy * dy; // 欧氏距离的平方
    }

    /*
     * 获取当前节点的邻居
     */
    List<AStarNode> getNeighbors(byte[][] mapData) {
        List<AStarNode> neighbors = new ArrayList<>();
        int x = getColumn();
        int y = getRow();

        // 上
        int ty = y - 1;
        if (isInMatrixRange(ty, x)) {
            neighbors.add(new AStarNode(ty, x, mapData));
        }

        // 右上
        int tx = x + 1;
        if (isInMatrixRange(ty, tx)) {
            neighbors.add(new AStarNode(ty, tx, mapData));
        }

        // 左上
        tx = x - 1;
        ty = y - 1;
        if (isInMatrixRange(ty, tx)) {
            neighbors.add(new AStarNode(ty, tx, mapData));
        }

        // 左
        if (isInMatrixRange(y, tx)) {
            neighbors.add(new AStarNode(y, tx, mapData));
        }

        // 右
        tx = x + 1;
        if (isInMatrixRange(y, tx)) {
            neighbors.add(new AStarNode(y, tx, mapData));
        }

        // 右下
        ty = y + 1;
        if (isInMatrixRange(ty, tx)) {
            neighbors.add(new AStarNode(ty, tx, mapData));
        }

        // 左下
        tx = x - 1;
        if (isInMatrixRange(ty, tx)) {
            neighbors.add(new AStarNode(ty, tx, mapData));
        }

        // 下
        if (isInMatrixRange(ty, x)) {
            neighbors.add(new AStarNode(ty, x, mapData));
        }

        return neighbors;
    }

    public boolean isInMatrixRange(int row, int column) {
        return (row >= 0 && row < 9) && (column >= 0 && column < 11);
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof AStarNode) {
            AStarNode n = (AStarNode) obj;
            return this.getRow() == n.getRow() && this.getColumn() == n.getColumn();
        } else {
            return false;
        }
    }

    public int getColumn() {
        return column;
    }

    public void setColumn(int column) {
        this.column = column;
    }

    public int getRow() {
        return row;
    }

    public void setRow(int row) {
        this.row = row;
    }

    @Override
    public int compareTo(AStarNode o) {
        return Integer.compare(this.F, o.F);
    }

    @Override
    public String toString() {
        return "AStarNode{" +
                "row=" + row +
                ", column=" + column +
                ", F=" + F +
                ", G=" + G +
                ", data=" + data +
                '}';
    }
}
